#include "GlobalData/GlobalData.h"
#include "Pathing/Pathfind.h"
#include <string>
#include <map>
#include <vector>
#include <cstdlib>

void PrintWalls(void)
{
	system("cls");
	for (int y = 0;y < GRID_HEIGHT;++y)
  {
	  for (int x = 0;x < GRID_WIDTH;++x)
	  {
		  if (gWorldMap.WorldGrid[y][x].Object != NULL)
			  std::cout << "*";
		  else if (gWorldMap.WorldGrid[y][x].IsWall)
			  std::cout << "0";
		  else
			  std::cout << " ";
	  }
	  std::cout << std::endl;
  }
	return;
}

int main(){
  gWindow.SetSize( 800, 800 );
  gWindow.Create();
  gWindow.ShowWnd( SW_SHOWNORMAL ); 
  gWindow.SetWndTitle( "CS380 Demo: The spoon strikes back!" );
  OpenGLSetup();

  srand((unsigned)time(0));

  // Register Mouse Procedures to Window
  gWindow.RegisterProc( WM_KEYDOWN, KeyDownProc );
  gWindow.RegisterProc( WM_KEYUP, KeyUpProc );
  gWindow.RegisterProc( WM_LBUTTONDOWN, LeftMouseDownProc );
  gWindow.RegisterProc( WM_LBUTTONUP, LeftMouseUpProc );
  gWindow.RegisterProc( WM_RBUTTONDOWN, RightMouseDownProc );
  gWindow.RegisterProc( WM_RBUTTONUP, RightMouseUpProc );
  gWindow.RegisterProc( WM_MOUSEMOVE, MouseMoveProc );

  // Register Key Procedures to Input
  gInput.RegisterDgtlProc( 'M', TRIGGERED, CurrentStateProc );
  gInput.RegisterDgtlProc( ' ', TRIGGERED, PauseSwitchProc );
  gInput.RegisterDgtlProc( 9, TRIGGERED, TabDownSwitchProc ); // Tab
  gInput.RegisterDgtlProc( 'P', TRIGGERED, ChangingAttributeSwitchProc );
  gInput.RegisterDgtlProc( 'O', TRIGGERED, TrackingSwitchProc );
  gInput.RegisterDgtlProc( WM_LBUTTONDOWN, TRIGGERED, AddObjectProc );
  gInput.RegisterDgtlProc( WM_RBUTTONDOWN, TRIGGERED, AddAgentProc );
  gInput.RegisterDgtlProc( '1', TRIGGERED, ChangeToFile1 );
  gInput.RegisterDgtlProc( '2', TRIGGERED, ChangeToFile2 );
  gInput.RegisterDgtlProc( '3', TRIGGERED, ChangeToFile3 );
  gInput.RegisterDgtlProc( '4', TRIGGERED, ChangeToFile4 );
  gInput.RegisterDgtlProc( '5', TRIGGERED, ChangeToFile5 );
  gInput.RegisterDgtlProc( '6', TRIGGERED, ChangeToFile6 );
  gInput.RegisterDgtlProc( '7', TRIGGERED, ChangeToFile7 );
  gInput.RegisterDgtlProc( '8', TRIGGERED, ChangeToFile8 );
  gInput.RegisterDgtlProc( '9', TRIGGERED, ChangeToFile9 );
  gInput.RegisterDgtlProc( 'L', TRIGGERED, ResetMap );
  gInput.RegisterDgtlProc( 'J', TRIGGERED, ResetObjects );
  gInput.RegisterDgtlProc( 'K', TRIGGERED, ResetAgents );

  AddGraphicObject( "grid.bmp", 0, 0, 9, 9 );

  // Steve's test code for pathfinding, create a bunch of walls and a banana
  ObjectModel* Wall[27];
  Wall[0] = new ObjectModel( "Wall.bmp", NewVector( 4.f, 7.f), NewVector( 1.f, 1.f) );
  Wall[1] = new ObjectModel( "Wall.bmp", NewVector(-7.f, 0.f), NewVector( 1.f, 7.f) );
  Wall[2] = new ObjectModel( "Wall.bmp", NewVector(-5.f, 1.f), NewVector( 1.f, 3.f) );
  Wall[3] = new ObjectModel( "Wall.bmp", NewVector(-5.f, 5.f), NewVector( 1.f, 1.f) );
  Wall[4] = new ObjectModel( "Wall.bmp", NewVector(-4.f,-4.f), NewVector( 2.f, 1.f) );
  Wall[5] = new ObjectModel( "Wall.bmp", NewVector(-3.f, 7.f), NewVector( 5.f, 1.f) );
  Wall[6] = new ObjectModel( "Wall.bmp", NewVector(-3.f, 5.f), NewVector( 1.f, 1.f) );
  Wall[7] = new ObjectModel( "Wall.bmp", NewVector(-3.f,-7.f), NewVector( 5.f, 1.f) );
  Wall[8] = new ObjectModel( "Wall.bmp", NewVector(-2.f, 4.f), NewVector( 1.f, 1.f) );
  Wall[9] = new ObjectModel( "Wall.bmp", NewVector(-2.f, 3.f), NewVector( 3.f, 1.f) );
  Wall[10] = new ObjectModel( "Wall.bmp", NewVector(-2.f,-1.f), NewVector( 3.f, 1.f) );
  Wall[11] = new ObjectModel( "Wall.bmp", NewVector(-2.f,-4.f), NewVector( 1.f, 1.f) );
  Wall[12] = new ObjectModel( "Wall.bmp", NewVector(-1.f, 5.f), NewVector( 2.f, 1.f) );
  Wall[13] = new ObjectModel( "Wall.bmp", NewVector(-1.f,-3.f), NewVector( 2.f, 1.f) );
  Wall[14] = new ObjectModel( "Wall.bmp", NewVector( 0.f, 2.f), NewVector( 1.f, 1.f) );
  Wall[15] = new ObjectModel( "Wall.bmp", NewVector( 0.f, 0.f), NewVector( 1.f, 1.f) );
  Wall[16] = new ObjectModel( "Wall.bmp", NewVector( 0.f,-4.f), NewVector( 1.f, 2.f) );
  Wall[17] = new ObjectModel( "Wall.bmp", NewVector( 0.f,-6.f), NewVector( 1.f, 1.f) );
  Wall[18] = new ObjectModel( "Wall.bmp", NewVector( 2.f, 5.f), NewVector( 1.f, 3.f) );
  Wall[19] = new ObjectModel( "Wall.bmp", NewVector( 2.f,-1.f), NewVector( 1.f, 2.f) );
  Wall[20] = new ObjectModel( "Wall.bmp", NewVector( 2.f,-3.f), NewVector( 1.f, 1.f) );
  Wall[21] = new ObjectModel( "Wall.bmp", NewVector( 2.f,-6.f), NewVector( 1.f, 2.f) );
  Wall[22] = new ObjectModel( "Wall.bmp", NewVector( 3.f, 2.f), NewVector( 2.f, 1.f) );
  Wall[23] = new ObjectModel( "Wall.bmp", NewVector( 4.f, 5.f), NewVector( 1.f, 2.f) );
  Wall[24] = new ObjectModel( "Wall.bmp", NewVector( 5.f, 0.f), NewVector( 3.f, 1.f) );
  Wall[25] = new ObjectModel( "Wall.bmp", NewVector( 6.f, 7.f), NewVector( 2.f, 1.f) );
  Wall[26] = new ObjectModel( "Wall.bmp", NewVector( 6.f, 6.f), NewVector( 1.f, 2.f) );

  // Register Walls with world
  for( int i = 0; i < 27; ++i ) {
    Wall[i]->brain.SetName( "DEFAULT_Wall" );
    gWorldMap.RegisterObject( *(Wall[i]), true );
  }

  while( Window::ActiveWndCount() ) {
    DWORD time = timeGetTime();

    // Procedures that must always be updated
    gWindow.HandleMsgs();
    gInput.Update();

    Draw( gGraphics.GetObjects() );
    gWindow.SwapBuffers();

    // Game Loop
    if( !gPause ) {
      // Steve changed this code to test pathfinding
      WorldMap::AgtList* agentList = gWorldMap.GetAgents();
      WorldMap::AgtIter agtIter = agentList->begin();
      WorldMap::AgtIter agtEnd = agentList->end();

      while( agtIter != agtEnd ) {
        AgentModel* agtPtr = (*agtIter);
        if( !agtPtr->UpdateMovement() ) {
          if( agtPtr->timer.IsPaused() ) {
            agtPtr->timer.StartTimer();
            
            if( agtPtr->pathingToObject ) {
              Vector2f pos = agtPtr->GetPosition();
              ObjectModel* obj = gWorldMap.WorldGrid[ (int)pos[1]+7 ][ (int)pos[0]+7 ].Object;
              bool isWall = gWorldMap.WorldGrid[ (int)pos[1]+7 ][ (int)pos[0]+7 ].IsWall;
              if( obj && !isWall )
                agtPtr->brain.UpdateWithObject( obj->brain );

              SmartAgent::RecepIter recpIter = agtPtr->brain.GetReceptors().begin();
              SmartAgent::RecepIter recpEnd = agtPtr->brain.GetReceptors().end();
              
              if( gTrackingOn && agtPtr == gTargetAgent ) {
                float total = 0;
                std::cout << "Updated agent: " << agtPtr->brain.GetName() << std::endl;
                while( recpIter != recpEnd ) {
                  std::cout << "  " << gSmartObjectIDGenerator.GetName( recpIter->attributeID )
                            << " " << recpIter->currentState << std::endl;
                  total += ABS(recpIter->currentState);
                  ++recpIter;
                }
                std::cout << "  - Average Amount: " << total / agtPtr->brain.GetReceptors().size() << std::endl;
                std::cout << std::endl;
              }
            }
            else {
              if( gTrackingOn && agtPtr == gTargetAgent ) {
                std::cout << "Updated agent: " << agtPtr->brain.GetName() << std::endl;
               std::cout << "  Random Path Complete:" << std::endl;
               std::cout << std::endl;
              }
            }
          }
          else {
            if( agtPtr->timer.GetTime() > 1000 ) {
              if( gChangeAttribute ) {
                SmartAgent::RecepSet recepSet = agtPtr->brain.GetReceptors();
                Receptor receptor;
                receptor.attributeID = gSmartObjectIDGenerator.GetId( gAttributeName );
                SmartAgent::RecepIter iter = recepSet.find( receptor );

                if( iter != recepSet.end() ) {
                  float   value = iter->currentState;
                  agtPtr->brain.ChangeReceptor( gAttributeName, value + 1, GenericStimRecOperation );
                }
              }

              // Create Search paths
			        agtPtr->timer.PauseTimer();
              PathFinder temp( (int)agtPtr->GetPosition()[0], (int)agtPtr->GetPosition()[1], 10.f );
              temp.Search();
              PathFinder::ObjList* paths = temp.GetObjectsList();
              
              // Evaluate Objects and find one with maximum value
              int index = 0, size = paths->size();

              float max = 0;        // The current maximum value
              int tracker = -1; // Tracks the best object path index
              int randTracker = 0;  // Tracks the random path index

              // Loop through remaining
              for( ; index < size; ++index ) {
                if( (*paths)[index].objPtr ) {
                  int pairCount = 0;
                  float newMax = agtPtr->brain.EvaluateObject( (*paths)[index].objPtr->brain, pairCount );

                  // Value objects with more pairs that match
                  newMax *= pairCount / (float)agtPtr->brain.GetReceptors().size();

                  if( newMax > max ) {
                    tracker = index;
                    max = newMax;
                  }
                }
                else {
                  randTracker = index;
                }
              }

              // Attach the path
              if( tracker >= 0 ) {
                agtPtr->AttachPath( (*paths)[tracker].Path );
                agtPtr->pathingToObject = true;
              }
              else {
                agtPtr->AttachPath( (*paths)[randTracker].Path );
                agtPtr->pathingToObject = false;
              }
            }
          }
        }
        ++agtIter;
      }
    }
	
    // Frame Rate Control
    while( (timeGetTime() - time) < 30 ) {
    }
  }

  gGraphics.ClearTextures();

  return 0;
}
